#ifndef THREAD_H
#define THREAD_H
#include <crtdbg.h>

template< typename T>
class CThread
{
protected:
   HANDLE m_hThread;
   DWORD m_dwThreadID;
   bool m_bSuspended;
   bool m_bAutoDel;

public:
   CThread() : m_hThread(NULL), m_dwThreadID(0), m_bSuspended(false), m_bAutoDel(false)
   {
   }

   virtual ~CThread()
   {
      Release();
   }
   
   void AutoDelete(bool bAutoDelete)
   {
		m_bAutoDel = bAutoDelete;
   }

   BOOL Start()
   {
	   if( !Create(ThreadProc, (LPVOID)static_cast<T*>(this)) ) return FALSE;
	   return TRUE;
   }
   
   virtual void Stop(DWORD dwTimeout = INFINITE)
   {
	   PostThreadMessage(m_dwThreadID, WM_QUIT, 0, 0);
	   if ( !WaitForThread(dwTimeout) ) Terminate();
	   Release();
   }

   BOOL Abort()
   {
	   if( m_hThread == NULL ) return FALSE;
	   if( m_bSuspended ) Resume();
	   PostThreadMessage(m_dwThreadID, WM_QUIT, 0, 0);
	   return TRUE;
   }

   static DWORD WINAPI ThreadProc(LPVOID pData)
   {
	   T* pThis = static_cast<T*>(pData);
	   DWORD dwRet = pThis->Run();
	   pThis->m_dwThreadID = 0;
	   if ( pThis->m_bAutoDel ) delete pThis;
	   return dwRet;
   }
  
   DWORD Run()
   {
	   _ASSERTE(false);
	   return 0;
   }

   BOOL Release()
   {
      if( m_hThread == NULL ) return TRUE;
      if( ::CloseHandle(m_hThread) == FALSE ) return FALSE;
      m_hThread = NULL;
      return TRUE;
   }

   BOOL SetPriority(int iPriority) const
   {
      _ASSERTE(m_hThread);
      return ::SetThreadPriority(m_hThread, iPriority);
   }

   int GetPriority() const
   {
      _ASSERTE(m_hThread);
      return ::GetThreadPriority(m_hThread);
   }

   BOOL Suspend()
   {
      _ASSERTE(m_hThread);
      if( m_bSuspended ) return TRUE;
      if( ::SuspendThread(m_hThread) == (DWORD) -1 ) return FALSE;
      m_bSuspended = true;
      return TRUE;
   }

   BOOL Resume()
   {
      _ASSERTE(m_hThread);
      if( !m_bSuspended ) return TRUE;
      if( ::ResumeThread(m_hThread) == (DWORD) -1 ) return FALSE;
      m_bSuspended = false;
      return TRUE;
   }

   BOOL IsSuspended() const
   {
      _ASSERTE(m_hThread);
      return m_bSuspended == true;
   }

   BOOL IsRunning() const
   {
      if( m_hThread == NULL ) return FALSE;
      DWORD dwCode = 0;
      ::GetExitCodeThread(m_hThread, &dwCode);
      return dwCode == STILL_ACTIVE;
   }

   BOOL WaitForThread(DWORD dwTimeout = INFINITE) const
   {
      _ASSERTE(m_hThread);
      return ::WaitForSingleObject(m_hThread, dwTimeout) == WAIT_OBJECT_0;
   }

   BOOL Terminate(DWORD dwExitCode = 0) const
   {
      _ASSERTE(m_hThread);
      return ::TerminateThread(m_hThread, dwExitCode);
   }

   DWORD GetThreadID() const
   {
      return m_dwThreadID;
   }

   operator HANDLE() const 
   { 
      return m_hThread; 
   }

private:

	BOOL Create(LPTHREAD_START_ROUTINE pThreadProc, LPVOID pParam = NULL, int iPriority = THREAD_PRIORITY_NORMAL)
	{
		_ASSERTE(m_hThread==NULL);
		_ASSERTE(pThreadProc);
		m_hThread = ::CreateThread(NULL, 0, pThreadProc, pParam, CREATE_SUSPENDED, &m_dwThreadID);
		if( m_hThread == NULL ) return FALSE;
		if( iPriority != THREAD_PRIORITY_NORMAL ) {
			if( !::SetThreadPriority(m_hThread, iPriority) ) {
				_ASSERTE(!"Couldn't set thread priority");
			}
		}
		return (::ResumeThread(m_hThread) != (DWORD) -1);
   }
};

#endif